package com.taichuan.selfcheck.hardware.audio;

import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.media.MediaPlayer;
import android.util.Log;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;

/**
 * 该类主要用于 使用 MediaPlayer 类进行声音的录制
 *
 * @author CamelLuo
 * @version 2021/3/25
 */
public class MediaPlayerClient {
    private static final String TAG = MediaPlayerClient.class.getSimpleName();

    /**
     * 播放音频资源的类型： 初始化状态
     */
    public static final int AUDIO_RESOURCES_TYPE_NONE = 0;
    /**
     * 播放音频资源的类型： raw文件夹下的资源id， 以R.raw.xxx 的形式读取
     */
    public static final int AUDIO_RESOURCES_TYPE_RAW_ID = 1;
    /**
     * 播放音频资源的类型： File文件路径
     */
    public static final int AUDIO_RESOURCES_TYPE_FILE_PATH = 2;

    /**
     * 当前正在播放音频的 资源类型
     */
    private int currentAudioResourcesType = AUDIO_RESOURCES_TYPE_NONE;
    /**
     * 当前正在播放 的 raw文件夹下的资源id
     */
    private int currentAudioRawId;
    /**
     * 当前正在播放 的 File文件路径
     */
    private String currentAudioFilePath;

    /**
     * 是否正处于播放状态
     */
    private boolean isInPlaying = false;

    /**
     * 多媒体播放核心类
     */
    private MediaPlayer mediaPlayer;

    /**
     * 多媒体播放状态回调监听
     */
    private MediaPlayerStateCallback callback;

    private Context context;


    public MediaPlayerClient(Context context) {
        this.context = context;
    }

    /**
     * 定义一个方法用于执行初始化 多媒体播放类 MediaPlayer
     */
    private void initMediaPlayer(int streamType){
        if (mediaPlayer != null){
            mediaPlayer.reset();
            Log.i(TAG, "initMediaPlayer: MediaPlayer 不为null，重置其状态");
        }else {
            mediaPlayer = new MediaPlayer();
            Log.i(TAG, "initMediaPlayer: MediaPlayer 为null，实例化对象");
        }
        //重置参数状态
        resetState();
        //设置播放音频的通道
        mediaPlayer.setAudioStreamType(streamType);
    }

    /**
     * 定义一个方法用于注销媒体播放器资源
     */
    public void destroy(){
        if (mediaPlayer != null){
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
            context = null;
        }
    }

    /**
     * 定义一个方法用于播放 raw文件夹下的资源id
     * @param resourcesId raw文件夹下的资源id，格式 R.raw.xxx
     * @param isNeedSyncPrepare 是否需要对音频资源进行异步准备，建议大型资源执行异步准备，否则可能会导致阻塞线程
     * @param isLooping 是否需要执行循环播放
     * @param streamType 播放该资源时使用的音频通道
     */
    public void playAudioByResourcesId(int resourcesId, boolean isNeedSyncPrepare,boolean isLooping, int streamType) {
        try {
            //构建MediaPlayer
            initMediaPlayer(streamType);
            //获取需要播放的raw文件的资源路径id
            AssetFileDescriptor assetFileDescriptor = context.getResources()
                    .openRawResourceFd(resourcesId);
            //设置播放资源
            mediaPlayer.setDataSource(assetFileDescriptor.getFileDescriptor(),
                    assetFileDescriptor.getStartOffset(), assetFileDescriptor.getLength());
            //释放资源
            assetFileDescriptor.close();
            //设置本次播放的音频资源类型
            setCurrentAudioResourcesType(AUDIO_RESOURCES_TYPE_RAW_ID);
            //记录本次准备播放的raw文件资源id
            setCurrentAudioRawId(resourcesId);

            //设置播放器参数并执行播放
            setMediaPlayerConfigAndPlay(isNeedSyncPrepare,isLooping);
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG, "playAudioByResourcesId: 执行 raw文件下 的id音频 资源 准备流程执行异常： " +e.getMessage());
        }
    }

    /**
     * 定义一个方法用于播放 File文件
     * @param audioFilePath File文件路径path
     * @param isNeedSyncPrepare 是否需要对音频资源进行异步准备，建议大型资源执行异步准备，否则可能会导致阻塞线程
     * @param isLooping 是否需要执行循环播放
     * @param streamType 播放该资源时使用的音频通道
     */
    public void playAudioByFilePath(String audioFilePath, boolean isNeedSyncPrepare,boolean isLooping, int streamType) {
        try {
            //构建MediaPlayer
            initMediaPlayer(streamType);
            //检查待播放的File文件是否存在
            File audioFile = new File(audioFilePath);
            if (audioFile.exists() && audioFile.isFile()){
                //设置播放资源
                FileInputStream is = new FileInputStream(audioFile);
                FileDescriptor fd = is.getFD();
                mediaPlayer.setDataSource(fd);

                //设置本次播放的音频资源类型
                setCurrentAudioResourcesType(AUDIO_RESOURCES_TYPE_FILE_PATH);
                //记录本次准备播放的raw文件资源id
                setCurrentAudioFilePath(audioFilePath);

                //设置播放器参数并执行播放
                setMediaPlayerConfigAndPlay(isNeedSyncPrepare,isLooping);
            }else {
                Log.e(TAG, "playAudioByFilePath: 传入的 音频文件路径不存在 或者不为File，无法执行播放");
            }
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG, "playAudioByResourcesId: 执行 File文件 音频资源 准备流程执行异常： " +e.getMessage());
        }
    }

    /**
     * 定义一个方法用于设置 媒体播放器的参数，监听器等，然后根据传入的配置决定如何执行资源准备并开始播放
     * @param isNeedSyncPrepare 是否需要对音频资源进行异步准备，建议大型资源执行异步准备，否则可能会导致阻塞线程
     * @param isLooping 是否需要执行循环播放
     */
    private void setMediaPlayerConfigAndPlay(boolean isNeedSyncPrepare,boolean isLooping){
        //设置是否循环播放
        mediaPlayer.setLooping(isLooping);

        //设置播放错误监听
        mediaPlayer.setOnErrorListener((mp, what, extra) -> {
            Log.e(TAG, "onError: 播放音频出现异常： 错误类型码 what： " + what + " 额外错误码 extra： " + extra);
            return false;
        });

        //设置播放完成监听
        mediaPlayer.setOnCompletionListener(mp -> {
            Log.e(TAG, "setMediaPlayerConfigAndPlay: 音频资源播放完成,本次播放的音频资源类型为： " + getCurrentAudioResourcesType()
            + " | 播放的raw资源id为： " + getCurrentAudioRawId() + " | 文件路径为： " + getCurrentAudioFilePath());
            //通知外部组件播放完毕
            if (callback != null){
                callback.onPlayCompleted(getCurrentAudioResourcesType(),getCurrentAudioRawId(),getCurrentAudioFilePath());
            }
        });

        //根据传入参数决定同步准备资源并播放 还是 异步准备资源并播放
        if (isNeedSyncPrepare){
            playAudioAfterPrepareFinish();
        }else {
            playAudio();
        }
    }

    /**
     * 定义一个方法用于执行 异步资源准备，并在准备完成后开始执行播放
     */
    private void playAudioAfterPrepareFinish(){
        //设置资源准备完成监听器，当资源准备完毕后，则执行播放
        mediaPlayer.setOnPreparedListener(mp -> {
            Log.i(TAG, "playAudioAfterPrepareFinish: 异步资源准备完毕，开始执行播放");
            //开始执行播放
            mp.start();
            //改变标志位，进入播放状态
            setInPlaying(true);
        });
        //执行异步资源准备，避免阻塞线程
        mediaPlayer.prepareAsync();
    }

    /**
     * 定义一个方法用于执行 同步资源准备，执行播放
     */
    private void playAudio() {
        try {
            //执行同步准备
            mediaPlayer.prepare();
            //开始执行播放
            mediaPlayer.start();
            //改变标志位，进入播放状态
            setInPlaying(true);
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(TAG, "playAudio: 执行同步资源准备，并播放音频资源异常： " + e.getMessage());
        }
    }

    /**
     * 定义一个方法用于 暂停播放 音频
     */
    public void pauseAudio(){
        if (isInPlaying()){
            if (mediaPlayer != null){
                if (mediaPlayer.isPlaying()){
                    mediaPlayer.pause();
                }else {
                    Log.e(TAG, "pauseAudio: mediaPlayer 当前不处于播放状态，无法执行 暂停播放 流程");
                }
            }else {
                Log.e(TAG, "pauseAudio: mediaPlayer 实例对象为null，无法执行 暂停播放 流程");
            }
        }else {
            Log.e(TAG, "pauseAudio: 当前不处于播放状态，无需执行 暂停播放 流程");
        }
    }

    /**
     * 定义一个方法用于 恢复播放 音频
     */
    public void resumeAudio(){
        if (isInPlaying()){
            if (mediaPlayer != null){
                mediaPlayer.start();
            }else {
                Log.e(TAG, "resumeAudio: mediaPlayer 实例对象为null，无法执行 恢复播放 流程");
            }
        }else {
            Log.e(TAG, "resumeAudio: 当前不处于播放状态中，无法执行 恢复播放 流程");
        }
    }

    /**
     * 定义一个方法用于 停止播放 音频
     */
    public void stopAudio(){
        if (isInPlaying()){
            if (mediaPlayer != null){
                mediaPlayer.stop();
                mediaPlayer.reset();
                //重置参数状态
                resetState();
                //改变标志位，停止播放
                setInPlaying(false);
            }else {
                Log.e(TAG, "stopAudio: mediaPlayer 实例对象为null，无法执行 停止播放 流程");
            }
        }else {
            Log.e(TAG, "stopAudio: 当前不处于播放状态中，无法执行 恢复播放 流程");
        }
    }

    /**
     * 定义一个方法用于 获取当前播放音频的播放位置
     * @return 当前播放的时间，单位ms
     */
    public int getCurrentPosition(){
        if (isInPlaying()){
            if (mediaPlayer != null){
                return mediaPlayer.getCurrentPosition();
            }else {
                Log.e(TAG, "getCurrentPosition: mediaPlayer 实例对象为null，无法执行 获取当前播放音频的播放位置 流程");
            }
        }else {
            Log.e(TAG, "getCurrentPosition: 当前不处于播放状态中，无法执行 获取当前播放音频的播放位置 流程");
        }
        return 0;
    }

    /**
     * 定义一个方法用于 获取当前播放音频的总时长
     * @return 当前播放音频的总时长，单位ms
     */
    public int getDuration(){
        if (isInPlaying()){
            if (mediaPlayer != null){
                return mediaPlayer.getDuration();
            }else {
                Log.e(TAG, "getDuration: mediaPlayer 实例对象为null，无法执行 获取当前播放音频的总时长 流程");
            }
        }else {
            Log.e(TAG, "getDuration: 当前不处于播放状态中，无法执行 获取当前播放音频的总时长 流程");
        }
        return 0;
    }


    /**
     * 定义一个方法用于重置 变量及标志位状态
     */
    private void resetState(){
        setCurrentAudioResourcesType(AUDIO_RESOURCES_TYPE_NONE);
        setCurrentAudioRawId(0);
        setCurrentAudioFilePath("");
    }


    public int getCurrentAudioResourcesType() {
        return currentAudioResourcesType;
    }

    public void setCurrentAudioResourcesType(int currentAudioResourcesType) {
        this.currentAudioResourcesType = currentAudioResourcesType;
    }

    public int getCurrentAudioRawId() {
        return currentAudioRawId;
    }

    public void setCurrentAudioRawId(int currentAudioRawId) {
        this.currentAudioRawId = currentAudioRawId;
    }

    public String getCurrentAudioFilePath() {
        return currentAudioFilePath;
    }

    public void setCurrentAudioFilePath(String currentAudioFilePath) {
        this.currentAudioFilePath = currentAudioFilePath;
    }

    public boolean isInPlaying() {
        return isInPlaying;
    }

    public void setInPlaying(boolean inPlaying) {
        isInPlaying = inPlaying;
    }

    /**
     * 定义要给方法用于设置 媒体播放器播放状态
     * @param callback
     */
    public void setMediaPlayerStateCallback(MediaPlayerStateCallback callback) {
        this.callback = callback;
    }

    /**
     * 定义一个接口用于外部监听 媒体播放器播放状态
     */
    public interface MediaPlayerStateCallback{

        /**
         * 音频资源播放完毕回调
         * @param audioResourcesType 本次播放完毕的音频资源类型，参见 MediaPlayerClient.AUDIO_RESOURCES_TYPE_RAW_ID
         *                              MediaPlayerClient.AUDIO_RESOURCES_TYPE_FILE_PATH
         * @see    MediaPlayerClient
         *
         * @param audioRawId 本次播放完毕的音频资源 的raw文件id值，仅在音频类型为 MediaPlayerClient.AUDIO_RESOURCES_TYPE_RAW_ID 时有意义
         * @param audioFilePath 本次播放完毕的音频资源 文件路径path，仅在音频类型为 MediaPlayerClient.AUDIO_RESOURCES_TYPE_FILE_PATH 时有意义
         */
        void onPlayCompleted(int audioResourcesType,int audioRawId,String audioFilePath);
    }
}
